skip_docs

devices = {
	"ios" => {
		15 => ["iPhone 13 Pro (15.5)", "iPad Pro (11-inch) (3rd generation) (15.5)",],
		16 => ["iPhone 14 Pro (16.4)", "iPad Pro (11-inch) (4th generation) (16.4)"],
		17 => ["iPhone 15 Pro (17.5)", "iPad Pro 11-inch (M4) (17.5)"],
		18 => ["iPhone 16 Pro (18.6)", "iPad Pro 11-inch (M4) (18.6)"],
		26 => ["iPhone 17 Pro (26.0)", "iPad Pro 11-inch (M4) (26.0)"],
	},
	"tvos" => {
		15 => ["Apple TV (15.4)"],
		16 => ["Apple TV (16.4)"],
		17 => ["Apple TV (17.5)"],
		18 => ["Apple TV (18.5)"],
		26 => ["Apple TV (26.0)"],
	},
	"watchos" => {
		8 => ["Apple Watch Series 7 (45mm) (8.5)"],
		9 => ["Apple Watch Series 8 (45mm) (9.4)"],
		10 => ["Apple Watch Series 9 (45mm) (10.5)"],
		11 => ["Apple Watch Series 10 (42mm) (11.5)"],
		26 => ["Apple Watch Series 11 (42mm) (26.0)"],
	},
	"visionos" => {
		1 => ["Apple Vision Pro (at 2732x2048) (1.2)"],
		2 => ["Apple Vision Pro (at 2732x2048) (2.5)"],
		26 => ["Apple Vision Pro (26.0)"],
	},
}

before_all do |lane, options|
	next if lane == :create_simulators
	create_simulators(platform: options[:platform], version: options[:version])
end

lane :create_simulators do |options|
	require 'json'
	require 'set'

	# map Fastfile platform keys to display names used by CoreSimulator runtimes
	platforms_to_os = {
		"ios" => "iOS",
		"tvos" => "tvOS",
		"watchos" => "watchOS",
		"visionos" => "visionOS",
	}

	# Build lookup tables from CoreSimulator for robust name→identifier mapping
	begin
		# Build a set of existing simulator name+runtime pairs to prevent duplicates across OS versions
		devices_json = sh("xcrun simctl list -j devices", log: false)
		devices_list = JSON.parse(devices_json)
		existing_pairs = Set.new
		(devices_list["devices"] || {}).each do |runtime_key, arr|
			Array(arr).each do |d|
				name = d["name"]
				next unless name && runtime_key
				existing_pairs.add("#{name}||#{runtime_key}")
			end
		end

		list_json = sh("xcrun simctl list -j", log: false)
		list = JSON.parse(list_json)
		devtypes = list["devicetypes"] || []
		runtimes = list["runtimes"] || []
	rescue => e
		UI.message("Failed to read simctl lists: #{e}")
		devtypes = []
		runtimes = []
		existing_pairs = Set.new
	end

	device_name_to_id = devtypes.each_with_object({}) do |dt, h|
		name = dt["name"]; id = dt["identifier"]
		h[name] = id if name && id
	end

	runtime_name_to_id = runtimes.each_with_object({}) do |rt, h|
		next unless rt["isAvailable"]
		name = rt["name"]; id = rt["identifier"]
		h[name] = id if name && id
	end

	# Fallback builders when exact matches are not present in the lookup tables
	build_device_type_id = proc do |device_name|
		s = device_name.gsub(/[()]/, '').gsub(/\s+/, '-').gsub(/[^A-Za-z0-9-]/, '')
		"com.apple.CoreSimulator.SimDeviceType.#{s}"
	end

	build_runtime_id = proc do |os_name, version|
		"com.apple.CoreSimulator.SimRuntime.#{os_name}-#{version.tr('.', '-')}"
	end

	platform_opt = options && options[:platform] ? options[:platform].to_s.downcase : nil
	version_opt  = options && options[:version] ? options[:version].to_i : nil

	local_devices = if platform_opt && devices.key?(platform_opt)
		subset_versions = devices[platform_opt]
		if version_opt && subset_versions.key?(version_opt)
			{ platform_opt => { version_opt => subset_versions[version_opt] } }
		else
			{ platform_opt => subset_versions }
		end
	else
		devices
	end

	local_devices.each do |platform, versions|
		os_name = platforms_to_os[platform]
		next if os_name.nil?

		versions.values.flatten.each do |descriptor|
			# descriptor examples:
			#   "iPhone 14 Pro (16.4)"
			#   "iPad Pro (11-inch) (4th generation) (16.4)"
			#   "Apple Vision Pro (2.5)"
			begin
				# Parse trailing "(x.y)" and derive device name
				if descriptor =~ /\s*\(([^()]+)\)\s*\z/
					runtime_version = $1
					device_name = descriptor.sub(/\s*\([^()]+\)\s*\z/, '')
				else
					UI.message("Could not parse runtime version from '#{descriptor}', skipping")
					next
				end

				runtime_name = "#{os_name} #{runtime_version}"

				device_type_id = device_name_to_id[device_name] || build_device_type_id.call(device_name)
				runtime_id = runtime_name_to_id[runtime_name] || build_runtime_id.call(os_name, runtime_version)

				# Use the device name without the version suffix as the simulator name
				sim_name = device_name

				pair_key = "#{sim_name}||#{runtime_id}"
				if existing_pairs.include?(pair_key)
					UI.message("Already exists: #{sim_name} (#{runtime_version}), skipping")
					next
				end

				sh(%(xcrun simctl create "#{sim_name}" "#{device_type_id}" "#{runtime_id}" || true))
				existing_pairs.add(pair_key)
			rescue => e
				UI.message("Skipping #{descriptor}: #{e}")
			end
		end
	end
end

lane :build do |options|
	platform = options[:platform].to_s.downcase
	version = options[:version].to_i
	scheme = options[:scheme].to_s

	unless scheme == "Showcase" || scheme == "SwiftUIIntrospect"
		raise "Unsupported scheme: #{scheme}"
		next
	end

	if platform == "macos"
		for destination in ["platform=macOS", "platform=macOS,variant=Mac Catalyst"]
			build_app(
				scheme: scheme,
				destination: destination,
				skip_archive: true,
				skip_codesigning: true,
				skip_package_ipa: true,
				skip_profile_detection: true,
			)
		end
	else
		run_tests(
			configuration: "Debug",
			build_for_testing: true,
			scheme: scheme,
			devices: devices[platform][version],
			prelaunch_simulator: false,
			ensure_devices_found: true,
			force_quit_simulator: true,
			disable_concurrent_testing: true,
		)
	end
end

lane :test do |options|
	configuration = (options[:configuration] || "Debug").to_s
	platform = options[:platform].to_s.downcase
	version = options[:version].to_i
	scheme = options[:scheme].to_s

	if platform == "macos"
		run_tests(
			configuration: configuration,
			scheme: scheme,
			destination: "platform=macOS",
			catalyst_platform: "macos",
			disable_slide_to_type: false,
			prelaunch_simulator: false,
			ensure_devices_found: true,
			force_quit_simulator: false,
			disable_concurrent_testing: true,
		)
	else
		unless ["SwiftUIIntrospectTests"].include?(scheme)
			raise "Unsupported scheme: #{scheme}"
		end
		run_tests(
			configuration: configuration,
			scheme: scheme,
			devices: devices[platform][version],
			prelaunch_simulator: false,
			ensure_devices_found: true,
			force_quit_simulator: true,
			reset_simulator: false,
			disable_concurrent_testing: true,
			result_bundle: true,
			output_directory: Dir.pwd + "/test_output",
		)
	end
end
